package cn.seaboot.admin.logger.service;

import cn.seaboot.commons.core.Asserts;
import cn.seaboot.commons.core.MapUtils;
import cn.seaboot.commons.exception.BizException;
import cn.seaboot.commons.exception.SystemError;
import cn.seaboot.commons.file.FileUtils;
import cn.seaboot.commons.file.FilenameUtils;
import cn.seaboot.commons.file.SaxUtils;
import cn.seaboot.commons.lang.P;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.*;
import java.nio.file.Files;
import java.util.*;

/**
 * log4j 管理
 *
 * @author Mr.css
 * @version 2023-03-17 16:10
 */
@Service
public class Log4jService {
    private final Logger logger = LoggerFactory.getLogger(Log4jService.class);

    /**
     * 日志存放位置
     */
    private File root = null;

    /**
     * 获取日志存放路径
     *
     * @return 文件对象
     */
    public File getRoot() {
        return root;
    }

    /**
     * 通过 SAX 解析，直接获取 log4j 日志的存放信息
     */
    @PostConstruct
    public void init() {
        ClassPathResource resource = new ClassPathResource("log4j2.xml");
        try (InputStream is = resource.getInputStream()) {
            SAXReader reader = new SAXReader();
            Document document = reader.read(is);

            // 获取根节点
            Element root = document.getRootElement();
            Element properties = root.element("Properties");

            Map<String, Object> values = SaxUtils.toMapByProperty(properties);
            String patternHome = MapUtils.getString(values, "pattern_home");
            String logHome = MapUtils.getString(values, "log_home");

            File file = new File(patternHome);

            this.root = new File(file.getAbsolutePath()).getParentFile();

            logger.debug("[--- logger ---] System log runtime directory is: {}", logHome);
            logger.debug("[--- logger ---] System log pattern directory is: {}", patternHome);
            logger.debug("[--- logger ---] System log output directory is: {}", this.root.getPath());
        } catch (IOException | DocumentException e) {
            throw new SystemError("[--- logger ---] Get system log output directory failed: ", e);
        }
    }

    /**
     * 是否是日志文件
     *
     * @param filename 文件名
     * @return if true
     */
    private boolean isLogFile(String filename) {
        return filename.endsWith(".log") || filename.endsWith(".gz");
    }

    /**
     * 获取文件列表
     *
     * @return 文件列表
     */
    public Set<String> listFile() {
        int len = root.getPath().length();
        Set<String> ret = new TreeSet<>();
        FileUtils.eachFile(root, file -> {
            if (isLogFile(file.getName())) {
                ret.add(file.getPath().substring(len));
            }
        });
        return ret;
    }

    /**
     * 手动清除 30 天之前的数据局
     *
     * @param filename 文件相对路径
     */
    public void deleteFile(String filename) {
        FilenameUtils.validFilePath(filename, "unsafe filename:" + filename);
        Asserts.state(isLogFile(filename), "it is not a log file: {}", filename);
        long time = new Date().getTime() - 30 * P.MS_DAY;
        File file = new File(root, filename);
        if (file.lastModified() < time) {
            if (!file.delete()) {
                logger.debug("[--- logger ---] remove file failed: {}", file.getPath());
            }
        } else {
            throw new BizException("[--- logger ---] it's a new log file, had better keep: " + filename);
        }
    }


    /**
     * 清除 30 天之前的数据局
     */
    public void cleanFile() {
        long time = new Date().getTime() - 30 * P.MS_DAY;

        FileUtils.eachFile(root, file -> {
            if (isLogFile(file.getName()) && file.lastModified() < time) {
                if (!file.delete()) {
                    logger.debug("[--- logger ---] clean file failed: {}", file.getPath());
                }
            }
        });
    }

    /**
     * 打开日志文件的IO输入
     *
     * @param filename 文件相对路径
     * @return is
     * @throws FileNotFoundException -
     */
    public InputStream openInputStream(String filename) throws IOException {
        FilenameUtils.validFilePath(filename, "Unsafe filename:" + filename);
        Asserts.state(isLogFile(filename), "It's not a log file: {}", filename);
        return Files.newInputStream(new File(root, filename).toPath());
    }
}
